$$ \newcommand{\floor}[1]{\left\lfloor{#1}\right\rfloor} \newcommand{\ceil}[1]{\left\lceil{#1}\right\rceil} \renewcommand{\mod}{\,\mathrm{mod}\,} \renewcommand{\div}{\,\mathrm{div}\,} \newcommand{\metar}{\,\mathrm{m}} \newcommand{\cm}{\,\mathrm{cm}} \newcommand{\dm}{\,\mathrm{dm}} \newcommand{\litar}{\,\mathrm{l}} \newcommand{\km}{\,\mathrm{km}} \newcommand{\s}{\,\mathrm{s}} \newcommand{\h}{\,\mathrm{h}} \newcommand{\minut}{\,\mathrm{min}} \newcommand{\kmh}{\,\mathrm{\frac{km}{h}}} \newcommand{\ms}{\,\mathrm{\frac{m}{s}}} \newcommand{\mss}{\,\mathrm{\frac{m}{s^2}}} \newcommand{\mmin}{\,\mathrm{\frac{m}{min}}} \newcommand{\smin}{\,\mathrm{\frac{s}{min}}} $$

Prijavi problem


Obeleži sve kategorije koje odgovaraju problemu

Još detalja - opišite nam problem


Uspešno ste prijavili problem!
Status problema i sve dodatne informacije možete pratiti klikom na link.
Nažalost nismo trenutno u mogućnosti da obradimo vaš zahtev.
Molimo vas da pokušate kasnije.

Како се праве анимације

Мењање цртежа

Као што знамо, функција која исцртава прозор је Form1_Paint. Да бисмо добили анимацију, потребно је омогућити да се ова функција често позива и да при томе не исцртава сваки пут исти цртеж. Цртежи које добијамо при позивима функције Form1_Paint ће се међусобно разликовати ако цртање зависи од вредности неких променљивих које су дефинисане ван свих функција (то јест као чланице класе формулара). Промена вредности тих променљивих довешће до другачијег цртежа.

Редовно мењање вредности ових променљивих и поновно исртавање цртежа можемо да остваримо помоћу тајмера. Подсетимо се, компонента тајмер се налази у групи „Компоненте” кутије за алат (Toolbox \(\to\) Components). У пројекат је можемо додати двокликом на њу, или кликом на њу а затим на формулар.

../_images/anim_timer.png

У примеру са штоперицом смо видели да су два важна својства тајмера Enabled и Interval, а догађај тајмера који желимо да контролишемо је Tick. Тајмер укључујемо и искључујемо постављањем његовог својства Enabled. Каже се да (укључен) тајмер „окида” (енгл. triggers) на сваких онолико милисекунди колико смо задали својством Interval. Ово окидање резултује извршавањем функције за обраду догађаја Tick.

За креирање анимације је довољно да у Функцији која обрађује догађај Tick тајмера променимо неке од вредности које утичу на цртање и позовемо функцију Invalidate(), захваљујући којој ће формулар бити поново исцртан. О функцији Invalidate() је било речи у првом примеру лекције о цртању помоћу петљи .

Ово ћемо радити у свим програмима са анимацијама и нећемо посебно истицати у сваком примеру да је потребно додати тајмер, укључити га и подесити му интервал.

Погледајмо како све ово изгледа на примеру.

Пример: Направити анимацију која наизменично приказује мање и веће срце.

../_images/anim_heart_smaller.png ../_images/anim_heart_bigger.png

Прво ћемо додати две слике срца и тајмер у наш пројекат. Након тога, поставићемо својство Enabled тајмера на true (укључујемо тајмер), а својство Interval на 500 (фрејмови ће се смењивати на 500 милисекунди). На крају, дефинисаћемо функцију за цртање и функцију за обраду догађаја Tick тајмера. Ево целог кода:

using System;
using System.Drawing;
using System.Windows.Forms;

namespace KucanjeSrca
{
    public partial class Form1 : Form
    {
        Image [] slika = { Properties.Resources.VeceSrce, Properties.Resources.ManjeSrce };
        int IndeksSlike = 0;

        public Form1()
        {
            InitializeComponent();
            ClientSize = new Size(200, 181);
            Text = "Srce";
            BackColor = Color.Blue;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            IndeksSlike = 1 - IndeksSlike;
            Invalidate();
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.DrawImage(slika[IndeksSlike], 0, 0);
        }
    }
}

Користимо променљиву IndeksSlike, која је чланица класе, па је видљива и употребљива у свим функцијама (методама) класе. Ова променљива добија само вредности 0 или 1 и користи се као индекс (редни број) слике у листи слика, која се састоји од две слике. На основу променљиве IndeksSlike се у програму одлучује која од две слике ће бити приказана. При сваком новом извршавању функције timer1_Tick, променљива IndeksSlike мења вредност (ако је била 0, добија вредност 1 и обрнуто), па се тиме мења и слика која ће бити приказана.

За променљиве од којих зависи цртање кажемо да описују сцену. Таквих променљивих може бити једна или више. У примеру са срцем сцену описује једна једина променљива, а то је променљива IndeksSlike.

У општем случају, када правимо нови фрејм анимације, користимо старе вредности променљивих које описују сцену, да бисмо израчунали њихове нове вредности. При томе, нове вредности могу, а не морају да буду различите од старих. Овај поступак зовемо ажурирање сцене.

Наравно, све променљиве које описују сцену морају да постоје (и памте своје вредности) и пре и после извршавања функције timer1_Tick. Због тога све ове променљиве и треба да буду чланице класе. За разлику од њих, променљиве направљене у самој функцији (локалне променљиве) постоје само за време извршавања функције и зато не могу послужити за памћење стања сцене.

Брзина анимације

Брзина анимације је одређена својством Interval тајмера. Можемо рећи да у примерима са анимацијом, својство Interval задаје трајање сваког фрејма у милисекундама. Брзина анимације се често мери и описује и бројем фрејмова који се приказују у јединици времена. За брзину смењивања фрејмова се користи скраћеница (јединица мере) фпс, што значи „фрејмова по секунди” (fps - frames per second).

У претходном програму сваки фрејм је трајао 500 милисекунди, што значи да смо користили 2 фрејма по секунди. Тиме смо добили ритам сличан ритму куцања срца. При томе смо јасно разликовали два фрејма који се наизменично појављују. Да бисмо добили утисак покрета, потребне су само веће брзине и више слика.

Уобичајено је да се за анимацију покрета користи најмање 15 фпс, јер при мањим брзинама приказивања покрет некоме може да делује испрекидано. Тако на пример ТВ емисије углавном користе 24 фпс, док се у данашње време за видео игре сматра да испод 30 фпс доживљај није довољно добар. Још брже анимације могу појединим посматрачима да дају још бољи ефекат, али су и скупље за прављење и приказивање.

Уколико у нашим програмима задамо веома велику брзину приказивања, може се догодити да наш рачунар не може да постигне такву брзину генерисања слика, а ни такву брзину приказивања. У том случају неће доћи до грешака, али ће стваран (ефективан) број фрејмова по секунди бити мањи (онај који рачунар може да постигне).

Анимација трчања из уводног текста може да се постигне програмом који је врло сличан примеру са срцем. Једина суштинска разлика је што се користи већи број слика (осам уместо две) и већа брзина приказивања.

../_images/anim_running1.png ../_images/anim_running2.png ../_images/anim_running3.png ../_images/anim_running4.png ../_images/anim_running5.png ../_images/anim_running6.png ../_images/anim_running7.png ../_images/anim_running8.png
using System;
using System.Drawing;
using System.Windows.Forms;

namespace Trcanje
{
    public partial class Form1 : Form
    {
        Image[] slika = {
            Properties.Resources.trcanje1, Properties.Resources.trcanje2,
            Properties.Resources.trcanje3, Properties.Resources.trcanje4,
            Properties.Resources.trcanje5, Properties.Resources.trcanje6,
            Properties.Resources.trcanje7, Properties.Resources.trcanje8
        };
        int IndeksSlike = 0;

        public Form1()
        {
            InitializeComponent();
            ClientSize = new Size(300, 264);
            Text = "Trčanje";
            BackColor = Color.White;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            IndeksSlike++;
            if (IndeksSlike == slika.Length)
                IndeksSlike = 0;

            Invalidate();
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.DrawImage(slika[IndeksSlike], 0, 0);
        }
    }
}

Испробајте различите брзине приказивања постављањем вредности својства Interval тајмера и видите како тај параметар утиче на изглед анимације. Наравно, за укупан доживљај је осим броја фрејмова у секунди важно и колико се разликују узастопне слике (већи број слика са мањим разликама даје бољи ефекат, али за то је протребна и већа брзина анимације).


Резимирајмо шта треба да урадите да бисте направили анимацију:

  • дефинишите променљиве које описују сцену као чланице класе (ти ће се подаци мењати током анимације);

  • дефинишите функцију timer1_Tick која обрађује откуцај тајмера тако што ажурира податке о ликовима и објектима на сцени, а затим позива функцију Invalidate()

  • дефинишите функцију Form1_Paint која исцртава сцену.

Анимације - питања

         Q-24: Поређајте брзине приказивања тако да редом одговарају трајањима фрејма од 10, 20, 50 и 100 милисекунди.

100 фпс
50 фпс
20 фпс
10 фпс
        

Задатак - предлог: Ако желите, пробајте да направите C# програм који ће у круг да приказује ваше одабране фотографије или неке друге слике по вашем избору.

Ако имате слике различите величине, или желите да их приказујете у величини различитој од оригиналне, погледајте остале функције DrawImage класе Graphics.

    Q-25: У примеру „трчање” је било потребно да променљива IndeksSlike узима редом у круг само оне вредности које одговарају позицијама слика у листи. У случају када имамо осам слика, те вредности су 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, итд. У општем случају, за n слика вредности су 0, 1, 2, … n-1, 0, 1, 2 итд.

    Подсетимо се, оператор % означава операцију рачунања остатка при дељењу. Помоћу ове операције можемо краћим записом да постигнемо исти циљ. Која од следећих наредби може равнопрано да замени овај део програма?

    IndeksSlike++;
    if (IndeksSlike == slika.Length)
        IndeksSlike = 0;
    
  • IndeksSlike = IndeksSlike + 1 % slika.Length;
  • Покушајте поново
  • IndeksSlike = (IndeksSlike % slika.Length) + 1;
  • Покушајте поново
  • IndeksSlike = (IndeksSlike + 1) % slika.Length;
  • Тачно
  • IndeksSlike = IndeksSlike % (slika.Length + 1);
  • Покушајте поново